//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <bulverde.h>
#include <INTCBits.h>
#include "drvpub.h"

#define IRQ_GPIO     10
#define GPIO_PINS    121

typedef struct {
    int  PinNumber;
    void * Handler;
    void * Device;
} GpioPin;

int g_gpioPinUsed = 0;
GpioPin g_gpioPins[GPIO_PINS];

static BOOL ValidatePin(long Pin)
{
    // Pin0, Pin1: Not use irq number IRQ_GPIO
    // Pin2, Pin5~8: Special use
    if (Pin == 3 || Pin == 4) return TRUE;
    if (Pin > 8 && Pin  < GPIO_PINS) return TRUE;
    return FALSE;
}

EXTERN_C BOOL Drv_GpioInstall(uint_t Pin, void *Device, void * Handler)
{
    if (!ValidatePin(Pin)) return FALSE;

    g_gpioPins[g_gpioPinUsed].PinNumber = (int)Pin;
    g_gpioPins[g_gpioPinUsed].Handler = Handler;
    g_gpioPins[g_gpioPinUsed].Device = Device;

    g_gpioPinUsed++;

    return TRUE;
}

INLINE ulong_t GetGedrValue(long RegIdx)
{
    UINT GedrValue;

    Drv_Assert(RegIdx >=0 && RegIdx < 4);
    switch (RegIdx) {
    case 0:
        GedrValue = GEDR0;
        break;
    case 1:
        GedrValue = GEDR1;
        break;
    case 2:
        GedrValue = GEDR2;
        break;
    default: // case 3:
        GedrValue = GEDR3;
        break;
    }

    return GedrValue;
}

// Interrupt routine for GPIO
void CDECL GpioIsr(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    long i, PinNumber;
    BOOL bDispatched = FALSE;

    for (i = 0; i < g_gpioPinUsed; i++) {
        long RegIndex, BitIndex;
        unsigned long GedrValue;
        typedef BOOL (*IsrHandler)(void *, BOOL *);

        PinNumber = g_gpioPins[i].PinNumber;
        RegIndex = (PinNumber >> 5) & 0x3;
        BitIndex = PinNumber & 0x1F; // The bit int the corresponding GEDR

        GedrValue = GetGedrValue(RegIndex);
        if ((GedrValue & (1 << BitIndex)) && g_gpioPins[i].Handler) {
            ((IsrHandler)g_gpioPins[i].Handler)(g_gpioPins[i].Device, NULL);
            bDispatched = TRUE;
            // Clear GPIO Status by the driver itself
        }

    }

    if (!bDispatched) {
        kprintf("Unknown ISR for GPIO!\n");
        kprintf("Dump GEDR: 0x%08x 0x%08x 0x%08x 0x%08x\n", GEDR0, GEDR1, GEDR2, GEDR3);
    }

//    INTC_GPIOXX_2_INT_EN(ICMR);

}

// Add the GPIO device to system
void GpioInit()
{
    long i;

    DzRegisterIsr(IRQ_GPIO, IPL7, (isr_t)GpioIsr, NULL);


    for (i = 0; i < GPIO_PINS; i++) {
        g_gpioPins[i].PinNumber = -1;
        g_gpioPins[i].Handler = NULL;
        g_gpioPins[i].Device = NULL;
    }

    return;
}

void Gpio_SetDir(int gpio_num, int direction)
{

    if (gpio_num < 32)
    {
        if (direction == GPIO_PIN_IN)
            GPDR0 &= ~(1 << (gpio_num % 32));
        if (direction == GPIO_PIN_OUT)
            GPDR0 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 64)
    {
        if (direction == GPIO_PIN_IN)
            GPDR1 &= ~(1 << (gpio_num % 32));
        if (direction == GPIO_PIN_OUT)
            GPDR1 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 96)
    {
        if (direction == GPIO_PIN_IN)
            GPDR2 &= ~(1 << (gpio_num % 32));
        if (direction == GPIO_PIN_OUT)
            GPDR2 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 120)
    {
        if (direction == GPIO_PIN_IN)
            GPDR3 &= ~(1 << (gpio_num % 32));
        if (direction == GPIO_PIN_OUT)
            GPDR3 |= (1 << (gpio_num % 32));
        return;
    }
}

void Gpio_SetAFR(int gpio_num, int function)
{
    if (gpio_num < 16)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR0_L &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR0_L &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR0_L |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR0_L &= ~(1 << ((gpio_num % 16) * 2));
            GAFR0_L |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR0_L |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 32)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR0_U &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR0_U &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR0_U |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR0_U &= ~(1 << ((gpio_num % 16) * 2));
            GAFR0_U |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR0_U |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 48)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR1_L &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR1_L &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR1_L |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR1_L &= ~(1 << ((gpio_num % 16) * 2));
            GAFR1_L |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR1_L |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 64)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR1_U &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR1_U &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR1_U |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR1_U &= ~(1 << ((gpio_num % 16) * 2));
            GAFR1_U |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR1_U |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 80)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR2_L &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR2_L &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR2_L |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR2_L &= ~(1 << ((gpio_num % 16) * 2));
            GAFR2_L |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR2_L |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 96)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR2_U &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR2_U = ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR2_U |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR2_U &= ~(1 << ((gpio_num % 16) * 2));
            GAFR2_U |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR2_U |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 112)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR3_L &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR3_L &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR3_L |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR3_L &= ~(1 << ((gpio_num % 16) * 2));
            GAFR3_L |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR3_L |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }
    if (gpio_num < 120)
    {
        if (function == GPIO_AS_GPIO)
        {

            GAFR3_U &= ~((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        if (function == GPIO_AS_AF1)
        {

            GAFR3_U &= ~(1 << ((gpio_num % 16) * 2 + 1));
            GAFR3_U |= (1 << ((gpio_num % 16) * 2));
        }
        if (function == GPIO_AS_AF2)
        {

            GAFR3_U &= ~(1 << ((gpio_num % 16) * 2));
            GAFR3_U |= (1 << ((gpio_num % 16) * 2 + 1));
        }
        if (function == GPIO_AS_AF3)
        {

            GAFR3_U |= ((1 << ((gpio_num % 16) * 2)) | (1 << ((gpio_num % 16) * 2 + 1)));
        }
        return;
    }

}

void Gpio_SetOutLow(int gpio_num)
{

    if (gpio_num < 32)
    {
        GPCR0 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 64)
    {
        GPCR1 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 96)
    {
        GPCR2 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 120)
    {
        GPCR3 |= (1 << (gpio_num % 32));
        return;
    }
}

void Gpio_SetOutHigh(int gpio_num)
{
    if (gpio_num < 32)
    {
        GPSR0 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 64)
    {
        GPSR1 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 96)
    {
        GPSR2 |= (1 << (gpio_num % 32));
        return;
    }
    if (gpio_num < 120)
    {
        GPSR3 |= (1 << (gpio_num % 32));
        return;
    }
}


class CGPIO : public Driver {
public:
    CARAPI Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent);
    virtual void Dispose();

};

CGPIO s_dummyGPIO;

ECode CGPIO::Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CGPIO::Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CGPIO::Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

void CGPIO::Dispose()
{
    return;
}

EXTERN IDeviceDriver * CDECL CreateGPIO(uint_t uDeviceNo, void *pvParameter)
{
    GpioInit();

    return &s_dummyGPIO;
}
